﻿#version 330 core
precision mediump float;

in vec3 normal_vector;
in vec3 frag_pos; // 片段的位置向量 (世界空间)
in vec2 tex_coords;
out vec4 frag_color;

// 定向光 默认白色光
struct DirectLight {
  vec3 direction;
  
  vec3 ambient;
  vec3 diffuse;
  vec3 specular;
};

// 点光源 默认白色
struct PointLight {
  vec3 position;

  vec3 ambient;
  vec3 diffuse;
  vec3 specular;

  float constant;
  float linear;
  float quadratic;
};

// 手电筒光源 默认白色
struct FlashLight {
  vec3 position;
  vec3 direction;
  // vec3 color;

  vec3 ambient;
  vec3 diffuse;
  vec3 specular;

  float constant;
  float linear;
  float quadratic;
  
  float cut_off;
  float outer_cut_off;
};
#define NR_POINT_LIGHTS 4

uniform vec3 view_pos; // 观察者向量
uniform DirectLight direct_light;
uniform PointLight point_light[NR_POINT_LIGHTS];
uniform FlashLight flash_light;
uniform sampler2D texture_diffuse1;

vec3 CalcDirectionalLight(DirectLight direct_light, vec3 norm, vec3 view_vec);
vec3 CalcPointLight(PointLight point_light, vec3 norm, vec3 frag_pos, vec3 view_vec);
vec3 CalcFlashLight(FlashLight flash_light, vec3 norm, vec3 frag_pos, vec3 view_vec);

void main(void) {
  vec3 norm = normalize(normal_vector); // 法向量
  vec3 view_vec = normalize(view_pos - frag_pos); // 观察片段向量

  // 定向光
  vec3 result = CalcDirectionalLight(direct_light, norm, view_vec);
  // 点光源
  for (int i = 0; i < NR_POINT_LIGHTS; ++i) {
	result += CalcPointLight(point_light[i], norm, frag_pos, view_vec);
  }
  // 聚光
  result += CalcFlashLight(flash_light, norm, frag_pos, view_vec);

  frag_color = vec4(result, 1.0f);
}

vec3 CalcDirectionalLight(DirectLight direct_light, vec3 norm, vec3 view_vec) {
  // 环境光照
  vec3 ambient = direct_light.ambient * texture(texture_diffuse1, tex_coords).rgb; // 环境分量
  // 漫反射光照
  vec3 light_vec = normalize(-direct_light.direction); // 定向光方向
  float diff = max(dot(norm, light_vec), 0.0f); // 防止夹角大于90度，漫反射分量为负
  vec3 diffuse = direct_light.diffuse * texture(texture_diffuse1, tex_coords).rgb * diff; // 漫反射分量 
  // 镜面光照
  vec3 reflect_vec = reflect(-light_vec, norm); // 反射要求第一个参数 光线向量的方向是光源指向物体
  float spec = pow(max(dot(view_vec, reflect_vec), 0.0f), 32.0f);
  vec3 specular = direct_light.diffuse * texture(texture_diffuse1, tex_coords).rgb * spec;

  return ambient + diffuse + specular;
}

vec3 CalcPointLight(PointLight point_light, vec3 norm, vec3 frag_pos, vec3 view_vec) {
  // 环境光照
  vec3 ambient = point_light.ambient * texture(texture_diffuse1, tex_coords).rgb; // 环境分量
  // 漫反射光照
  vec3 light_vec = normalize(point_light.position - frag_pos); // 光线方向 指向物体
  float diff = max(dot(norm, light_vec), 0.0f); // 防止夹角大于90度，漫反射分量为负
  vec3 diffuse = point_light.diffuse * texture(texture_diffuse1, tex_coords).rgb * diff; // 漫反射分量 
  // 镜面光照
  vec3 reflect_vec = reflect(-light_vec, norm); // 反射要求第一个参数 光线向量的方向是光源指向物体
  float spec = pow(max(dot(view_vec, reflect_vec), 0.0f), 32.0f);
  vec3 specular = point_light.diffuse * texture(texture_diffuse1, tex_coords).rgb * spec;

  // 点光源 光线衰减
  float dist = length(point_light.position - frag_pos);
  float attenuation = 1.0 / (point_light.constant + point_light.linear * dist + point_light.quadratic * (dist * dist));
  return (ambient + diffuse + specular) * attenuation;
}

vec3 CalcFlashLight(FlashLight flash_light, vec3 norm, vec3 frag_pos, vec3 view_vec) {
  // 环境光照
  vec3 ambient = flash_light.ambient * texture(texture_diffuse1, tex_coords).rgb; // 环境分量
  // 漫反射光照
  vec3 light_vec = normalize(flash_light.position - frag_pos); // 光线方向 指向物体
  float diff = max(dot(norm, light_vec), 0.0f); // 防止夹角大于90度，漫反射分量为负
  vec3 diffuse = flash_light.diffuse * texture(texture_diffuse1, tex_coords).rgb * diff; // 漫反射分量 
  // 镜面光照
  vec3 reflect_vec = reflect(-light_vec, norm); // 反射要求第一个参数 光线向量的方向是光源指向物体
  float spec = pow(max(dot(view_vec, reflect_vec), 0.0f), 32.0f);
  vec3 specular = flash_light.diffuse * texture(texture_diffuse1, tex_coords).rgb * spec;

  // 点光源 光线衰减
  float dist = length(flash_light.position - frag_pos);
  float attenuation = 1.0 / (flash_light.constant + flash_light.linear * dist + flash_light.quadratic * (dist * dist));
  ambient *= attenuation;
  diffuse *= attenuation;
  specular *= attenuation;

  // 平滑、软化边缘
  float theta = dot(light_vec, normalize(-flash_light.direction));
  float epsilon = flash_light.cut_off - flash_light.outer_cut_off; // 余弦值差（ϵ=ϕ−γ）
  float intensity = clamp((theta - flash_light.outer_cut_off) / epsilon, 0.0f, 1.0f);
  diffuse *= intensity;
  specular *= intensity;
  
  return ambient + diffuse + specular;
}
